/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.simengine.odeevents;

import java.util.Collection;
import org.apache.commons.math.ode.events.EventException;
import org.apache.commons.math.ode.events.EventHandler;
import simhya.model.store.Function;
import simhya.model.store.Predicate;
import simhya.matheval.SymbolArray;
import simhya.model.transition.AtomicReset;
import simhya.dataprocessing.EventCollector;
import simhya.simengine.ProgressMonitor;
import simhya.simengine.GuardChecker;


/**
 *
 * @author luca
 */
public class TimedEvent implements EventHandler, PrintableEvent {
    private int id;
    private Function firingTime;
    private Predicate guard;
    private SymbolArray vars;
    private AtomicReset [] resets;
    private boolean isStopping;
    private boolean isGuarded;
    private int dimension;
    private boolean useCache = false;
    private EventCollector collector;
    String name = "Fixed time event";
    private boolean printEvent;
    private ProgressMonitor monitor;
    private Collection<Integer> listOfModifiedTriggers;
    private Collection<Integer> listOfModifiedPriorities;
    private GuardChecker odeFunction;

    /**
     * constructs a new timed event, with given firing time function, depending
     * on continuous variables, with empty reset, no guards and not stopping.
     * The proper setters has to be called to complete the initialization.
     * @param dimension the number of continuous variables
     * @param firingTime a function returning the next firing time.
     */
    public TimedEvent(int id, int dimension,  Function firingTime, GuardChecker ode) {
        this.id = id;
        this.odeFunction = ode;
        this.dimension = dimension;
        this.firingTime = firingTime;
        this.guard = null;
        this.resets = new AtomicReset[0];
        this.isStopping = false;
        this.isGuarded = false;
        this.vars = SymbolArray.getNewFastEvalSymbolArray(dimension);
        this.collector = null;
        this.printEvent = false;
        this.monitor = null;
        listOfModifiedTriggers = null;
        listOfModifiedPriorities = null;
    }
    
    
        /**
     * Sets a monitor to print event occurrences to
     * @param monitor a ProgressMonitor or null to deactivate the functionality
     */
    public void setPrintEvent(ProgressMonitor monitor) {
        if (monitor != null) {
            this.monitor = monitor;
            this.printEvent = true;
        } else {
            this.monitor = null;
            this.printEvent = false;
        }
    }

    public void setCollectorInfo(String name, EventCollector collector) {
        this.collector = collector;
        this.name = name;
    }


    public void setUseCache(boolean useCache) {
        this.useCache = useCache;
    }

    public void setGuard(Predicate guard) {
        this.guard = guard;
        this.isGuarded = guard != null && !guard.isTautology() ? true : false;
    }

    public void setIsStopping(boolean isStopping) {
        this.isStopping = isStopping;
    }

    public void setReset(AtomicReset[] resets) {
        this.resets = resets != null ? resets : new AtomicReset[0];
    }

    

    public int eventOccurred(double t, double[] y, boolean increasing) throws EventException {
        if (this.printEvent)
            monitor.signalEvent(name, t);
        if (collector!= null)
            collector.putNextTimedEvent(t, name);
        if (isStopping)
            return STOP;
        else
            return RESET_STATE;
    }

    public double g(double t, double[] y) throws EventException {
        vars.setValuesReference(y);
        double x;
        if (this.isGuarded) {
            boolean  g = useCache ? guard.evaluateCache(vars) :
                           guard.evaluate(vars);
            if (g)
                x = useCache ? firingTime.computeCache(vars) :
                               firingTime.compute(vars);
            else
                x = Double.POSITIVE_INFINITY;
        } else
            x = useCache ? firingTime.computeCache(vars) :
                               firingTime.compute(vars);
        return t-x;
    }

    public void resetState(double t, double[] y) throws EventException {
        vars.setValuesReference(y);
        for (AtomicReset r : resets)
            if (useCache)
                r.computeNewValuesCache(vars);
            else
                r.computeNewValues(vars);
        for (AtomicReset r : resets)
            r.updateStoreVariables(y);
        //updates ode function 
        odeFunction.updateGuardStatus(id, t, y);
    }

    
    public double getPriority(double t, double[] y) throws EventException {
        return 1.0;
    }

    @Override
    public String toString() {
        return "TimedEvent{" + "name=" + name + '}';
    }

    public Integer getId() {
        return id;
    }

    public Collection<Integer> getIdsOfEventsWithModifiedPriority() {
        return this.listOfModifiedPriorities;
    }

    public Collection<Integer> getIdsOfEventsWithModifiedTrigger() {
        return this.listOfModifiedTriggers;
    }

    public boolean isEnabled(double t, double[] y) {
        if (isGuarded) {
            vars.setValuesReference(y);
            return (useCache ?  guard.evaluateCache(vars) : guard.evaluate(vars)) 
                    || guard.changeContinuously();
        } else {
            return true;
        }
    }
    
    
    public void setIdsOfEventsWithModifiedPriority(Collection<Integer> list) {
        this.listOfModifiedPriorities = list;
    }

    public void setIdsOfEventsWithModifiedTrigger(Collection<Integer> list) {
        this.listOfModifiedTriggers = list;
    }
    
}
